package com.ljj.admin.shiro.services;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * {@code HashedCredentialMatcher}支持对提供的{@code AuthenticationToken}凭据进行哈希处理，然后再与数据存储中{@code AuthenticationInfo}中的凭据进行比较。
 *
 *在保护用户的专用凭据（密码，密钥等）时，凭据哈希是最常见的安全技术之一。大多数开发人员从不希望以任何人都可以查看的形式存储用户凭证，因此他们经常在将用户凭证保存到数据存储中之前对其进行哈希处理。
 * 此类（及其子类）的功能如下：
 * 哈希用户登录期间提供的{@code AuthenticationToken}凭据。
 * 直接将此哈希值与存储在系统中的{@code AuthenticationInfo}凭据进行比较（存储的帐户凭据应该已经是哈希形式）。
 * 如果这两个值是{@link #equals（Object，Object）equal}，则提交的凭据匹配，否则不匹配
 *
 *
 */
@Service
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

    // 密码错误重复登录次数
    private int reTryCountSet = 3;

    public void setReTryCount(int reTryCount) {
        this.reTryCountSet = reTryCount;
    }

    private Cache<String, AtomicInteger> passwordRetryCache;

    public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        String username = (String) token.getPrincipal();
        AtomicInteger retryCount = passwordRetryCache.get(username);
        if (retryCount == null) {
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }


        // 先做验证  验证错误 则尝试次数加1  验证成功 清楚 验证错误
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {

            //clear retry count
            passwordRetryCache.remove(username);
        }else
        {
            if (retryCount.incrementAndGet() > reTryCountSet) {
                // 抛出异常信息
                throw new ExcessiveAttemptsException("密码出错次数以达到最大值");
            } else {
                passwordRetryCache.put(username, retryCount);
            }

        }



        return matches;

    }

}
